﻿using System;
using System.Net;
using System.Windows;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Chisel.Core
{
    /// <summary>
    /// Actor types that can have styles applied.
    /// </summary>
    public enum ActorType { Entity = 128, Connection = 256, Item = 512};

    /// <summary>
    /// What a brush is used for.
    /// </summary>
    public enum BrushType { Foreground = 2048, Background = 4096, Text = 9082 };

    /// <summary>
    /// Provides rendering styles to a canvas.
    /// </summary>
    public class StyleProvider
    {

        public StyleProvider()
        {
			// defaulf hovering/selected color
			var hs = new Color() {
				R = 0,
				G = 128,
				B = 220,
				A = 255
			};

            // default entity brushes
            SetBrush(ActorType.Entity, BrushType.Foreground, ActorStateFlag.None, new SolidColorBrush(Colors.Black));
            SetBrush(ActorType.Entity, BrushType.Foreground, ActorStateFlag.Hovering, new SolidColorBrush(Colors.Green));
            SetBrush(ActorType.Entity, BrushType.Foreground, ActorStateFlag.Selected, new SolidColorBrush(Colors.Blue));
            SetBrush(ActorType.Entity, BrushType.Foreground, ActorStateFlag.Selected | ActorStateFlag.Hovering, new SolidColorBrush(hs));

            // default item brushes
            SetBrush(ActorType.Item, BrushType.Background, ActorStateFlag.Hovering,
				GenerateGradient(Colors.White, Colors.Green, Orientation.Vertical, 0.2));
            SetBrush(ActorType.Item, BrushType.Background, ActorStateFlag.Selected,
                GenerateGradient(Colors.Blue, Colors.White, Orientation.Vertical, 0.2));
            SetBrush(ActorType.Item, BrushType.Background, ActorStateFlag.Selected | ActorStateFlag.Hovering,
				GenerateGradient(hs, Colors.White, Orientation.Vertical, 0.2));

			// default connection brushes
			SetBrush(ActorType.Connection, BrushType.Foreground, ActorStateFlag.None, new SolidColorBrush(Colors.Black));
			SetBrush(ActorType.Connection, BrushType.Foreground, ActorStateFlag.Hovering, new SolidColorBrush(Colors.Green));
			SetBrush(ActorType.Connection, BrushType.Foreground, ActorStateFlag.Selected, new SolidColorBrush(Colors.Blue));
			SetBrush(ActorType.Connection, BrushType.Foreground, ActorStateFlag.Selected | ActorStateFlag.Hovering, new SolidColorBrush(hs));

			FontSize = 10;

			EntityRadius = 8;

			ItemGroupBrush = GenerateGradient(Colors.White, Colors.Gray, Orientation.Vertical , 0.2);

			ConnectionStyle = ConnectionStyle.Square;
			ConnectionThickness = 2;
        }


        #region Brushes

        /// <summary>
        /// Generate a linear gradient with the given colors.
        /// </summary>
        public static LinearGradientBrush GenerateGradient(Color startColor, Color endColor, Orientation orientation, double opacity)
        {
            return new LinearGradientBrush()
            {
                GradientStops = new GradientStopCollection()
                {
                    new GradientStop() {Color = startColor, Offset = 1},
                    new GradientStop() {Color = endColor, Offset = 0}
                },
                MappingMode = BrushMappingMode.RelativeToBoundingBox,
                StartPoint = new Point(0, 0),
                EndPoint = new Point(0, 1),
                Opacity = opacity
            };
        }

        /// <summary>
        /// Generate a linear gradient with the given colors.
        /// </summary>
        /// <remarks>Defaults to completely opaque.</remarks>
        public static LinearGradientBrush GenerateGradient(Color startColor, Color endColor, Orientation orientation)
        {
            return GenerateGradient(startColor, endColor, orientation, 1.0);
        }

        /// <summary>
        /// Brush used if there isn't one defined for an actor type and state.
        /// </summary>
        private Dictionary<BrushType, Brush> _defaultBrushes = new Dictionary<BrushType, Brush> {
            {BrushType.Foreground, new SolidColorBrush(Colors.Black)},
            {BrushType.Background, null},
            {BrushType.Text, new SolidColorBrush(Colors.Black)}
        };

        private Dictionary<int, Brush> _brushes = new Dictionary<int, Brush>();


        /// <summary>
        /// Sets a brush to be used with the given actor type and state.
        /// </summary>
        public void SetBrush(ActorType actorType, BrushType brushType, ActorState state, Brush brush)
        {
            SetBrush(actorType, brushType, state.Flags, brush);
        }

        /// <summary>
        /// Sets a brush to be used with the given actor type and state.
        /// </summary>
        public void SetBrush(ActorType actorType, BrushType brushType, ActorStateFlag stateFlag, Brush brush)
        {
            _brushes[(int)actorType + (int)brushType + (int)stateFlag] = brush;
        }

        /// <summary>
        /// Gets a brush to be used with the given actor type and state.
        /// </summary>
        public Brush GetBrush(ActorType actorType, BrushType brushType, ActorState state)
        {
            Brush brush = null;
            int flags = (int)state.Flags;
            // try to get the exact one first
            if (_brushes.TryGetValue((int)actorType + (int)brushType + flags, out brush))
                return brush;
            else // try to get one that's close
            {
                var flagVals = EnumHelper.GetValues<ActorStateFlag, int>();
                Array.Reverse(flagVals);
                foreach (var flagVal in flagVals)
                {
                    if ((flags & flagVal) > 0 && _brushes.TryGetValue((int)actorType + (int)brushType + flagVal, out brush))
                        return brush;
                }
            }
            return _defaultBrushes[brushType];
        }

		/// <summary>
		/// Gets the brush for the given actor, brush type, and state.
		/// </summary>
		public Brush GetBrush(Actor actor, BrushType brushType, ActorState state)
		{
			if (actor is Entity)
				return GetBrush(ActorType.Entity, brushType, state);
			if (actor is Item)
				return GetBrush(ActorType.Item, brushType, state);
			if (actor is Connection)
				return GetBrush(ActorType.Connection, brushType, state);
			throw new Exception("Don't know how to get the brush for an actor of type " + actor.GetType().ToString());
		}

        #endregion


		#region Fonts

		/// <summary>
		/// Font size to use for regular text in the actors.
		/// </summary>
		public double FontSize { get; set; }

		#endregion


		#region Entities

		/// <summary>
		/// The corner radius of entities.
		/// </summary>
		public double EntityRadius { get; set; }

		#endregion


		#region Items

		/// <summary>
		/// Brush to fill the item groups.
		/// </summary>
		public Brush ItemGroupBrush { get; set; }

		#endregion


		#region Connections

		/// <summary>
		/// The style used to draw all connections on the canvas.
		/// </summary>
		public ConnectionStyle ConnectionStyle { get; set; }

		/// <summary>
		/// The thickness of the connection lines.
		/// </summary>
		public double ConnectionThickness { get; set; }
		
		#endregion

	}
}
